/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bef.core.be;

import com.inspur.edp.bef.api.action.assembler.IAbstractActionAssemblerFactory;
import com.inspur.edp.bef.api.action.determination.BeDeterminationTypes;
import com.inspur.edp.bef.api.action.validation.BeValidationTypes;
import com.inspur.edp.bef.api.be.IBENodeEntity;
import com.inspur.edp.bef.api.be.IBENodeEntityContext;
import com.inspur.edp.bef.core.action.base.ActionFactory;
import com.inspur.edp.bef.core.action.base.BEEntityActionExecutor;
import com.inspur.edp.bef.core.action.determination.basetypes.childtrans.BeAftCreateChildTransDtmAdapter;
import com.inspur.edp.bef.core.action.determination.basetypes.childtrans.BeChildTransAftLoadDtmAdapter;
import com.inspur.edp.bef.core.action.determination.basetypes.childtrans.BeChildTransAftModifyDtmAdapter;
import com.inspur.edp.bef.core.action.determination.basetypes.childtrans.BeChildTransB4SaveDtmAdapter;
import com.inspur.edp.bef.core.action.determination.basetypes.childtrans.BeChildTransOnCancelDtmAdapter;
import com.inspur.edp.bef.core.action.determination.basetypes.childtrans.BeUdtTransOnCancelDtmAdapter;
import com.inspur.edp.bef.core.action.save.SaveContext;
import com.inspur.edp.bef.core.coderule.CodeRuleCompensater;
import com.inspur.edp.bef.core.action.tcc.EntityCustomTccExecutor;
import com.inspur.edp.bef.core.action.validation.basetypes.childtrans.BeAftModifyChildTransValAdapter;
import com.inspur.edp.bef.core.action.validation.basetypes.childtrans.BeAfterSaveChildTransValAdapter;
import com.inspur.edp.bef.core.action.validation.basetypes.childtrans.BeB4SaveChildTransValAdapter;
import com.inspur.edp.bef.core.be.builtinimpls.BeEntityCacheInfo;
import com.inspur.edp.bef.core.determination.BefB4SaveDtmExecutor;
import com.inspur.edp.bef.core.determination.BefOnCancelDtmExecutor;
import com.inspur.edp.bef.core.determination.builtinimpls.BefAfterLoadingDtmAssembler;
import com.inspur.edp.bef.core.determination.builtinimpls.BefAfterModifyDtmAssembler;
import com.inspur.edp.bef.core.determination.builtinimpls.BefB4SaveDtmAssembler;
import com.inspur.edp.bef.core.determination.builtinimpls.BefOnCancelDtmAssembler;
import com.inspur.edp.bef.core.determination.builtinimpls.BefRetrieveDefaultDtmAssembler;
import com.inspur.edp.bef.core.determination.builtinimpls.adaptors.BefAfterModifyCodeRuleDtmAdaptor;
import com.inspur.edp.bef.core.determination.builtinimpls.adaptors.BefOnCancelCodeRuleDtmAdaptor;
import com.inspur.edp.bef.core.action.tcc.builtinimpls.BefTccChildTransHandler;
import com.inspur.edp.bef.core.tcc.builtinimpls.BefTccHandlerAssembler;
import com.inspur.edp.bef.core.validation.AfterSaveValidationExecutor;
import com.inspur.edp.bef.core.validation.builtinimpls.BefAfterModifyValAssembler;
import com.inspur.edp.bef.core.validation.builtinimpls.BefAfterSaveValAssembler;
import com.inspur.edp.bef.core.validation.builtinimpls.BefB4SaveValAssembler;
import com.inspur.edp.bef.core.validation.builtinimpls.adaptors.BefAftSaveValAdaptor;
import com.inspur.edp.bef.spi.action.AbstractAction;
import com.inspur.edp.bef.spi.action.assembler.entityAssemblerFactory.AbstractActionAssemblerFactory;
import com.inspur.edp.bef.spi.entity.CodeRuleAssembler;
import com.inspur.edp.bef.spi.entity.CodeRuleInfo;
import com.inspur.edp.bef.spi.entity.IDefaultValueDic;
import com.inspur.edp.bef.spi.extend.IBEEntityDataExtend;
import com.inspur.edp.bef.spi.entity.builtinimpls.BefEntityResInfoImpl;
import com.inspur.edp.caf.transaction.api.annoation.tcc.BusinessActionContext;
import com.inspur.edp.cdp.coderule.api.CodeRuleBEAssignService;
import com.inspur.edp.cef.api.dataType.entity.ICefEntity;
import com.inspur.edp.cef.entity.changeset.ChangeType;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.repository.DataSaveResult;
import com.inspur.edp.cef.core.datatype.CefEntity;
import com.inspur.edp.cef.core.datatype.CefEntityCacheInfo;
import com.inspur.edp.cef.core.determination.builtinimpls.CefAfterModifyDtmAssembler;
import com.inspur.edp.cef.core.determination.builtinimpls.CefB4SaveDtmAssembler;
import com.inspur.edp.cef.core.determination.builtinimpls.CefRetrieveDefaultDtmAssembler;
import com.inspur.edp.cef.core.validation.builtinimpls.CefAfterModifyValAssembler;
import com.inspur.edp.cef.core.validation.builtinimpls.CefB4SaveValAssembler;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.Util;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.spi.determination.IEntityRTDtmAssembler;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.DataTypePropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.ObjectType;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.UdtPropertyInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.EntityResInfo;
import com.inspur.edp.cef.spi.entityaction.CefDataTypeAction;
import com.inspur.edp.cef.spi.extend.datatype.ICefDataExtend;
import com.inspur.edp.cef.spi.extend.datatype.IDataExtendContainer;
import com.inspur.edp.cef.spi.extend.entity.ICefAddedChildDataExtend;
import com.inspur.edp.cef.spi.extend.entity.ICefAddedChildEntityExtend;
import com.inspur.edp.cef.spi.extend.entity.ICefEntityExtend;
import com.inspur.edp.cef.spi.validation.IEntityRTValidationAssembler;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.var;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

public abstract class BENodeEntity extends CefEntity implements IBENodeEntity {

  @Override
  public IBENodeEntityContext getContext() {
    return (IBENodeEntityContext) super.getContext();
  }

  private static CodeRuleBEAssignService service;

  public static CodeRuleBEAssignService getCodeRuleInstance() {
    if (service == null) {
      service = SpringBeanUtils.getBean(CodeRuleBEAssignService.class);
    }
    return service;
  }

  public IBENodeEntityContext getBEContext() {
    return getContext();
  }

  protected BENodeEntity(String dataId) {
    privateID = dataId;
  }

  public  boolean isRoot()
  {return false;}

  private final String privateID;

  @Override
  public String getID() {
    return privateID;
  }

  @Override
  protected final CefEntityCacheInfo getEntityCacheInfo() {
    return getBeEntityCacheInfo();
  }

  protected BeEntityCacheInfo getBeEntityCacheInfo()
  {
    throw new RuntimeException();
  }
  // region execute Action
  @Override
  protected <TResult> TResult execute(CefDataTypeAction<TResult> action) {
    DataValidator.checkForNullReference(action, "action");

    BEEntityActionExecutor<TResult> tempVar = new BEEntityActionExecutor<TResult>(getContext());
    tempVar.setAction((AbstractAction<TResult>) action);
    return tempVar.execute();
  }

  protected final <TResult> TResult executeNoSession(AbstractAction<TResult> action) {
    DataValidator.checkForNullReference(action, "action");

    action.setContext(getContext());
    action.internalExecute();
    return action.getResult();
  }

  public void generateChildCode(SaveContext ctx, IEntityData data, HashMap<String, Object> map,
      String beID) {
    if (data == null || data.getChilds().size()==0) {
      return;
    }
    for (Map.Entry<String, IEntityDataCollection> childCollection : data.getChilds().entrySet()) {

      if (childCollection.getValue().size() == 0) {
        continue;
      }
      for (IEntityData childData : childCollection.getValue()) {
        //子表实体
        BENodeEntity childEntity = (BENodeEntity) getChildBEEntity(childCollection.getKey(),
            childData.getID());
        childEntity.beforeSaveCodeGenerate(ctx, map,beID);
      }
    }
  }

  @Override
  public void assignDefaultValue(IEntityData data) {
    for(Map.Entry<String, DataTypePropertyInfo> entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet()){
      if(entry.getValue().getDefaultValue() != null){
        switch (entry.getValue().getDefaultValue().getValueType()){
          case Value:
            data.setValue(entry.getValue().getPropertyName(), entry.getValue().getDefaultValue().getRealValue());
            break;
          case Expression:
            data.setValue(entry.getValue().getPropertyName(), entry.getValue().getDefaultValue().getRealValue());
            break;
        }
      }
    }
  }
  public void beforeSaveCodeGenerate(SaveContext ctx, HashMap<String, Object> map,String beID) {
    IChangeDetail tranChange = getBEContext().getTransactionalChange();
    if (tranChange == null) {
      return;
    }
    if (getData() == null) {
      return;
    }
    IEntityData data = (IEntityData) getData();
    map.put(getBEContext().getCode(), data);
    if (tranChange.getChangeType() == ChangeType.Added) {
      generateMainCode(ctx, map,beID);
    }
    if (tranChange.getChangeType() == ChangeType.Added
        || tranChange.getChangeType() == ChangeType.Modify) {
      generateChildCode(ctx, data, map,beID);
    }
    //TODO 删除时，释放编号规则，需要讨论一下，暂不修改
    if (tranChange.getChangeType() == ChangeType.Deleted) {
      //service.release();
    }
  }

  public void generateMainCode(SaveContext ctx, HashMap<String, Object> map,String beID) {

    java.util.HashMap<String, CodeRuleInfo> codeInfo = getBeforeSaveCodeRule();
    if (codeInfo == null) {
      return;
    }

    for (var key : codeInfo.keySet()) {

      var currentCodeValue = this.getData().getValue(key);
      String stringValue = (currentCodeValue instanceof String) ? (String) currentCodeValue : null;
      if (currentCodeValue != null && stringValue != null && !stringValue.equals("")) {
        continue;
      }
      //
      String code = getCodeRuleInstance().generate(
          beID,
          getContext().getCode(),
          key,
          map,
          codeInfo.get(key).getCodeRuleId());
      ctx.addCompensater(new CodeRuleCompensater(this, key));
      //当前一行子表数据的编码规则赋值
      this.getData().setValue(key, code);
    }
  }

public <TResult> TResult executeAction(AbstractAction<TResult> action) {
    return execute(action);
  }

  // endregion

  // region Determination
  // public virtual void afterModifyDeterminate()
  // {
  //    var ass = GetAfterModifyDtmAssembler();
  //    if (ass == null)
  //        return;

  //    new AfterModifyDtmExecutor(ass, Context).execute();
  // }

  @Override
  public final void afterModifyValidate() {
    super.afterModifyValidate();
  }

  // TODO:[cef]通过Executor
  @Override
  public final void afterLoadingDeterminate() {
    executeAction(new ActionFactory().getAfterLoadingDtmAction(getContext(),extList));
  }

  @Override
  public final void onCancelDeterminate() {
    IEntityRTDtmAssembler ass = getOnCancelDtmAssembler();
    if (ass == null) {
      return;
    }
    if(extList != null)
    {
      for(ICefEntityExtend ext:extList){
        ext.addExtDtm(ass, BeDeterminationTypes.OnCancel);
      }
    }
    new BefOnCancelDtmExecutor(ass, getBEContext()).execute();
  }

  private IEntityRTDtmAssembler getOnCancelDtmAssembler() {
    if(getVersion()==0)
      return createOnCancelDtmAssembler();
    if(getBeEntityCacheInfo().getOnCancelDtmAssembler()!=null)
      return getBeEntityCacheInfo().getOnCancelDtmAssembler();
    BefOnCancelDtmAssembler onCancelDtmAssembler = (BefOnCancelDtmAssembler) createOnCancelDtmAssembler();
    addOnCancelDtm(onCancelDtmAssembler);
    addBuiltInOnCancelDtms(onCancelDtmAssembler);
    getBeEntityCacheInfo().setOnCancelDtmAssembler(onCancelDtmAssembler);
    return onCancelDtmAssembler;
  }

  protected void addOnCancelDtm(BefOnCancelDtmAssembler onCancelDtmAssembler){}

  private void addBuiltInOnCancelDtms(BefOnCancelDtmAssembler onCancelDtmAssembler) {
    addUdtTransOnCancelDtms(onCancelDtmAssembler);
    addChildTransOnCancelDtms(onCancelDtmAssembler);
    addCodeRuleCancelDtms(onCancelDtmAssembler);
  }

  private void addCodeRuleCancelDtms(BefOnCancelDtmAssembler onCancelDtmAssembler){
    onCancelDtmAssembler.addBelongingDetermination(new BefOnCancelCodeRuleDtmAdaptor());
  }
  private void addChildTransOnCancelDtms(BefOnCancelDtmAssembler onCancelDtmAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      onCancelDtmAssembler.addChildAssembler(
          new BeChildTransOnCancelDtmAdapter(entityResInfoEntry.getValue().getEntityCode(),
              entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  private void addUdtTransOnCancelDtms(BefOnCancelDtmAssembler onCancelDtmAssembler) {
    int index =0;
    for(Map.Entry<String, DataTypePropertyInfo> entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet())
    {
      if(entry.getValue().getObjectType() != ObjectType.UDT)
        continue;
      DataTypePropertyInfo udtPropertyInfo= (DataTypePropertyInfo) entry.getValue();
      onCancelDtmAssembler.insertBelongingDetermination(
        new BeUdtTransOnCancelDtmAdapter(udtPropertyInfo.getPropertyName(),
            udtPropertyInfo.getPropertyName(),
            ((UdtPropertyInfo) udtPropertyInfo.getObjectInfo()).getUdtConfigId()), index++);
    }
  }

  @Override
  public void beforeSaveDeterminate() {
    beforeSaveDeterminate(getContext().getCurrentTransactionalChange());
  }

  @Override
  public void beforeSaveDeterminate(IChangeDetail change) {
    IEntityRTDtmAssembler ass = getBeforeSaveDtmAssembler();
    if (ass == null) {
      return;
    }

    if(extList != null){
      for(ICefEntityExtend ext:extList){
        ext.addExtDtm(ass, BeDeterminationTypes.BeforeSave);
      }
    }

    checkEntityContext();
    new BefB4SaveDtmExecutor(ass, getContext(), change).execute();
  }

  public IEntityRTDtmAssembler createAfterLoadingDtmAssembler() {
    return new BefAfterLoadingDtmAssembler();
  }


  public IEntityRTDtmAssembler getAfterLoadingDtmAssembler() {
    if(getVersion()==0)
      return createAfterLoadingDtmAssembler();
    if(getBeEntityCacheInfo().getAfterLoadingDtmAssembler()!=null)
      return getBeEntityCacheInfo().getAfterLoadingDtmAssembler();
    BefAfterLoadingDtmAssembler afterLoadingDtmAssembler = (BefAfterLoadingDtmAssembler) createAfterLoadingDtmAssembler();
    addAfterLoadingDtm(afterLoadingDtmAssembler);
    addBuiltInAfterLoadingDtms(afterLoadingDtmAssembler);
    getBeEntityCacheInfo().setAfterLoadingDtmAssembler(afterLoadingDtmAssembler);
    return afterLoadingDtmAssembler;
  }

  protected void addAfterLoadingDtm(BefAfterLoadingDtmAssembler afterLoadingDtmAssembler){}

  private void addBuiltInAfterLoadingDtms(BefAfterLoadingDtmAssembler afterLoadingDtmAssembler) {
    addChildTransAfterLoadingDtms(afterLoadingDtmAssembler);
  }

  private void addChildTransAfterLoadingDtms(BefAfterLoadingDtmAssembler afterLoadingDtmAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      afterLoadingDtmAssembler.addChildAssembler(
          new BeChildTransAftLoadDtmAdapter(entityResInfoEntry.getValue().getEntityCode(),
              entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  public IEntityRTDtmAssembler createOnCancelDtmAssembler() {
    return new BefOnCancelDtmAssembler();
  }

  public void afterSaveValidate(DataSaveResult saveResult) {
    IEntityRTValidationAssembler ass = getAfterSaveValAssembler();

    if (ass == null) {
      return;
    }

    if(extList != null){
      for(ICefEntityExtend ext:extList){
        ext.addExtVal(ass, BeValidationTypes.AfterSave);
      }
    }

    new AfterSaveValidationExecutor(ass, getBEContext(), saveResult).execute();
  }

  private IEntityRTValidationAssembler getAfterSaveValAssembler() {
    if (this.getVersion() == 0) {
      return this.createAfterSaveValAssembler();
    } else if (this.getBeEntityCacheInfo().getAfterSaveValAssembler() != null) {
      return this.getBeEntityCacheInfo().getAfterSaveValAssembler();
    } else {
      BefAfterSaveValAssembler assembler = (BefAfterSaveValAssembler)this.createAfterSaveValAssembler();
      addAfterSaveVal(assembler);
      this.addChildTransAfterSaveVals(assembler);
      this.getBeEntityCacheInfo().setAfterSaveValAssembler(assembler);
      return assembler;
    }
  }

  protected void addAfterSaveVal(BefAfterSaveValAssembler assembler){}

  protected void addChildTransAfterSaveVals(BefAfterSaveValAssembler assembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      assembler.getChildAssemblers().add(
          new BeAfterSaveChildTransValAdapter(entityResInfoEntry.getValue().getEntityCode(),
              entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  protected IEntityRTValidationAssembler createAfterSaveValAssembler() {
    return new BefAfterSaveValAssembler();
  }
// endregion

  // region Modify
  @Override
  public void modify(IChangeDetail change) {
    throw new NotImplementedException();
  }

  // endregion

  // region 组装器工厂
  private IAbstractActionAssemblerFactory actionAssemblerFactory;

protected IAbstractActionAssemblerFactory createAssemblerFactory() {
    return null;
  }

@Override
  public IAbstractActionAssemblerFactory getAssemblerFactory() {
    return (actionAssemblerFactory != null)
        ? actionAssemblerFactory
        : (actionAssemblerFactory =
            ((createAssemblerFactory()) != null)
                ? createAssemblerFactory()
                : new AbstractActionAssemblerFactory());
  }

public IDefaultValueDic getDefaultValueDic() {
    return null;
  }

  // endregion

  // region ChildEntity

@Override
  public final ICefEntity getChildEntity(String childCode, String id) {
    return getChildBEEntity(childCode, id);
  }

  protected IBENodeEntityContext createChildEntityContext(String childCode, String id) {
    BENodeEntityContext tempVar = new BENodeEntityContext();
    tempVar.setID(id);
    tempVar.setCode(childCode);
    tempVar.setParentContext(getContext());
    if (getContext().getCurrentData() != null) {
      tempVar.setCurrentData(getContext().getCurrentData().getChilds().get(childCode).tryGet(id));
    }

    IChangeDetail currentTemplateChange = getContext().getCurrentTemplateChange();
    if (currentTemplateChange != null) {
      tempVar.setCurrentTemplateChange(
          Util.getChildChange(currentTemplateChange, childCode, id));
    }

    IChangeDetail currentChange = getContext().getCurrentChange();
    if (currentChange != null) {
      tempVar.setCurrentChange(Util.getChildChange(currentChange, childCode, id));
    }

    if (getContext().getTransactionData() != null) {
      tempVar.setTransactionData(
          getContext().getTransactionData().getChilds().get(childCode).tryGet(id));
    }

    IChangeDetail transactionalChange = getContext().getTransactionalChange();
    if (transactionalChange != null) {
      tempVar.setTransactionalChange(
          Util.getChildChange(transactionalChange, childCode, id));
    }

    if (getContext().getOriginalData() != null) {
      tempVar.setOriginalData(getContext().getOriginalData().getChilds().get(childCode).tryGet(id));
    }

    tempVar.setModelResInfo(getContext().getModelResInfo());
    tempVar.setSessionItem(
        (getContext() instanceof BENodeEntityContext)
            ? ((BENodeEntityContext) getContext()).getSessionItem()
            : ((BEContext) getContext()).getSessionItem());
    return tempVar;
    // TODO: 主子节点Context是两个实现类
  }

  // endregion

  @Override
  public IEntityData createEntityData(String id) {
    IEntityData result = innerCreateEntityData(id);
    if(id != null && !id.equals(result.getID())) {
      result.setID(id);
    }
    if (extList != null) {
      ((IDataExtendContainer) result).addExtend(
          extList.stream().map(item -> item.getDataExtend()).collect(Collectors.toList())
              .toArray(new ICefDataExtend[extList.size()]));
    }
    return result;
  }

  protected IEntityData innerCreateEntityData(String id) {
    throw new RuntimeException();
  }

  public IEntityData createChildEntityData(String childCode, String id) {
    Objects.requireNonNull(childCode, childCode);
    var extendedChild = getExtendedChildData(childCode);
    if (extendedChild != null) {
      var data = (IEntityData) extendedChild.createData();
      data.setID(id);
      return data;
    }
    IEntityData data = innerCreateChildEntityData(childCode, id);
    if(id != null && !id.equals(data.getID())) {
      data.setID(id);
    }
    if (extList != null) {
      ((IDataExtendContainer) data).addExtend(
          extList.stream().map(item -> {
            ICefEntityExtend childEntity = item.getChildEntity(childCode);
            return childEntity != null ? childEntity.getDataExtend() : null;
          })
          .filter(item -> item != null)
          .collect(Collectors.toList())
          .toArray(new ICefDataExtend[0]));
    }
    return data;
  }

  public IEntityData innerCreateChildEntityData(String childCode, String id) {
    throw new RuntimeException();
  }

  @Override
  public IEntityData createChildAccessor(String childCode, IEntityData data) {
    var extendedChild = getExtendedChildData(childCode);
    if (extendedChild != null) {
      return (IEntityData) extendedChild.createAccessor(data);
    }
    IEntityData rez = innerCreateChildAccessor(childCode, data);
    return rez;
  }

  public IEntityData innerCreateChildAccessor(String childCode, IEntityData data) {
    throw new RuntimeException();
  }

  @Override
  public IBENodeEntity getChildBEEntity(String childCode, String id) {
    Objects.requireNonNull(childCode, "childCode");

    var extendedChild = getExtendedChild(childCode);
    if (extendedChild != null) {
      var childEntity = (BENodeEntity) extendedChild.createEntity(id);
      childEntity.setContext(this.createChildEntityContext(childCode, id));
      childEntity.setParent(this);
      return childEntity;
    }

    IBENodeEntity rez = innerGetChildBEEntity(childCode, id);
    if(rez.getContext() == null) {
      rez.setContext(createChildEntityContext(childCode, id));
      ((BENodeEntity)rez).setParent(this);
    }
    if (extList != null) {
      ((BENodeEntity) rez).setExtend(
          extList.stream().map(item -> item.getChildEntity(childCode))
              .filter(item -> item != null)
              .collect(Collectors.toList()));
    }
    return rez;
  }

  private ICefAddedChildEntityExtend getExtendedChild(String childCode) {
    if (extList != null) {
      for (var ext : extList) {
        var addedChild = ext.getAddedChilds();
        if (addedChild == null || addedChild.isEmpty()) {
          continue;
        }
        var childExtend = addedChild.stream().filter(item -> childCode.equals(item.getNodeCode()))
            .findFirst();
        if (childExtend.isPresent()) {
          return childExtend.get();
        }
      }
    }
    return null;
  }

  private ICefAddedChildDataExtend getExtendedChildData(String childCode) {
    if (extList != null) {
      for(var ext : extList) {
        IBEEntityDataExtend dataExt = (IBEEntityDataExtend)ext.getDataExtend();
        if(dataExt == null){
          continue;
        }
        var addedChild = dataExt.getAddedChilds();
        if (addedChild == null || addedChild.isEmpty()) {
          continue;
        }
        var childExtend = addedChild.stream().filter(item -> childCode.equals(item.getCode()))
            .findFirst();
        if (childExtend.isPresent()) {
          return childExtend.get();
        }
      }
    }
    return null;
  }

  protected IBENodeEntity innerGetChildBEEntity(String childCode, String id) {
    throw new RuntimeException("not implemented");
  }

  protected CodeRuleAssembler getCodeRuleAssembler() {
    return null;
  }

  public final java.util.HashMap<String, CodeRuleInfo> getBeforeSaveCodeRule() {
   if(getVersion()>0)
    return ((BefEntityResInfoImpl)getEntityResInfo()).getBeforeSaveCodeRules();

    CodeRuleAssembler assembler = getCodeRuleAssembler();
    if (assembler == null) {
      return null;
    }
    return assembler.getBeforeSave();
  }

  public final java.util.HashMap<String, CodeRuleInfo> getAfterCreateCodeRule() {
    if(getVersion()>0)
      return ((BefEntityResInfoImpl)getEntityResInfo()).getAfterCreateCodeRules();

    CodeRuleAssembler assembler = getCodeRuleAssembler();
    if (assembler == null) {
      return null;
    }
    return assembler.getAfterCreate();
  }

  //region [运行时定制]扩展
//  private List<IBEEntityExtend> extList;
//
//  public final void setExtend(List<IBEEntityExtend> extList) {
//    this.extList = extList;
//  }

  @Override
  public Object executeBizAction(String actionCode, Object... pars) {
    throw new RuntimeException("生成型工程不支持 executeBizAction");
  }
  //endregion


  @Override
  protected final void addChildTransRetrieveDefaultDtms(
      CefRetrieveDefaultDtmAssembler retrieveDefaultDtmAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      retrieveDefaultDtmAssembler.addChildAssembler(new BeAftCreateChildTransDtmAdapter(entityResInfoEntry.getValue().getEntityCode(),entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  @Override
  protected void addExtendAftModifyDtms(CefAfterModifyDtmAssembler afterModifyDtmAssembler) {
    afterModifyDtmAssembler.addBelongingDetermination(new BefAfterModifyCodeRuleDtmAdaptor());
  }



  @Override
  protected void addChildTransAfterModifyDtms(CefAfterModifyDtmAssembler afterModifyDtmAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      afterModifyDtmAssembler.addChildAssembler(new BeChildTransAftModifyDtmAdapter(entityResInfoEntry.getValue().getEntityCode(),entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  @Override
  protected void addChildTransAfterModifyVals(CefAfterModifyValAssembler afterModifyValAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      afterModifyValAssembler.getChildAssemblers().add(
          new BeAftModifyChildTransValAdapter(entityResInfoEntry.getValue().getEntityCode(),
              entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  @Override
  protected void addChildTransB4SaveDtms(CefB4SaveDtmAssembler b4SaveDtmAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      b4SaveDtmAssembler.addChildAssembler(
          new BeChildTransB4SaveDtmAdapter(entityResInfoEntry.getValue().getEntityCode(),
              entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  @Override
  protected void addExtendB4SaveVals(CefB4SaveValAssembler b4SaveValAssembler) {
    addUQConstraintCheckVals(b4SaveValAssembler);
  }

  private void addUQConstraintCheckVals(CefB4SaveValAssembler b4SaveValAssembler) {

  }

  @Override
  protected void addChildTransB4SaveVals(CefB4SaveValAssembler b4SaveValAssembler) {
    if(getEntityResInfo().getChildEntityResInfos()==null||getEntityResInfo().getChildEntityResInfos().size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry:getEntityResInfo().getChildEntityResInfos().entrySet())
    {
      b4SaveValAssembler.getChildAssemblers().add(
          new BeB4SaveChildTransValAdapter(entityResInfoEntry.getValue().getEntityCode(),
              entityResInfoEntry.getValue().getEntityCode()));
    }
  }

  @Override
  protected IEntityRTDtmAssembler createAfterModifyDtmAssembler() {
    return new BefAfterModifyDtmAssembler();
  }

  @Override
  protected IEntityRTDtmAssembler createRetrieveDefaultDtmAssembler() {
    return new BefRetrieveDefaultDtmAssembler();
  }

  @Override
    protected IEntityRTDtmAssembler createBeforeSaveDtmAssembler() {
    return new BefB4SaveDtmAssembler();
  }

  @Override
  protected IEntityRTValidationAssembler createAfterModifyValidationAssembler() {
    return new BefAfterModifyValAssembler();
  }

  @Override
  protected IEntityRTValidationAssembler createBeforeSaveValidationAssembler() {
    return new BefB4SaveValAssembler();
  }

  //region tcc
  public void tccCancelByCustom(BusinessActionContext tccContext, IChangeDetail reverseChg) {
    BefTccHandlerAssembler assembler = getTccAssembler();
    new EntityCustomTccExecutor(getBEContext(), reverseChg, assembler, tccContext, false).execute();
  }

  public void tccConfirmByCustom(BusinessActionContext tccContext, IChangeDetail reverseChg) {
    BefTccHandlerAssembler assembler = getTccAssembler();
    new EntityCustomTccExecutor(getBEContext(), reverseChg, assembler, tccContext, true).execute();
  }

  private BefTccHandlerAssembler getTccAssembler() {
    if(getBeEntityCacheInfo().getTccAssembler() == null) {
      BefTccHandlerAssembler tccAssembler = createTccAssembler();
      tccAssembler.setNodeCode(getContext().getCode());
      addBuiltInTccHandler(tccAssembler);
      getBeEntityCacheInfo().setTccAssembler(tccAssembler);
    }
    return getBeEntityCacheInfo().getTccAssembler();
  }


  private void addBuiltInTccHandler(BefTccHandlerAssembler assembler) {
    //childTrans
    Map<String, EntityResInfo> childInfos = getEntityResInfo().getChildEntityResInfos();
    if(childInfos ==null|| childInfos.size()==0)
      return;
    for(Map.Entry<String, EntityResInfo> entityResInfoEntry: childInfos.entrySet()) {
      assembler.addChildHandler(
          new BefTccChildTransHandler(entityResInfoEntry.getValue().getEntityCode()));
    }
  }


  protected BefTccHandlerAssembler createTccAssembler() {
    return new BefTccHandlerAssembler();
  }
  //endregion
}
